package cn.pso.nspsofs;


import cn.read.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;

import static cn.util.RealDistributionHelper.inverseCumulativeProbability;

public class Particle_NSPSOFS {
    private final Logger log = LoggerFactory.getLogger(Particle_NSPSOFS.class);

    private static final float X_MIN = 0;
    private static final float X_MAX = 1;

    // 粒子的位置，求解问题多少维，则此数组为多少维
    public double[] pos;
    // 粒子的速度，维数同位置
    public double[] v;
    public double[] fitness;// 粒子的适应度
    public double[] pbest;// 粒子的历史最好位置
    public double[] gbest;// 所有粒子找到的最好位置
    public double randnum1;
    public double randnum2;

    public static int dims;
    public static double w;
    public static double c1;
    public static double c2;

    // 历史最优解
    public double[] gbest_fitness;
    // 当前个体最优解
    public double[] pbest_fitness;

    private double[][] bounds;

    /**
     * 初始化粒子
     *
     * @param dim  表示粒子的维数
     * @param nObj 求解目标数量
     */
    public void initial(int dim, int nObj, FN fn) {
        dims = dim;
        pos = new double[dim];
        v = new double[dim];

        pbest = new double[dim];
        gbest = new double[dim];

        fitness = new double[nObj];                    //适应值，这里有2个，第一个是特征数，第二个是错误率
        pbest_fitness = new double[nObj];
        gbest_fitness = new double[nObj];

        double[][] bounds = fn.doGetBounds(dim);
        for (int i = 0; i < pos.length; i++) {
            double[] bound = bounds[i];
            pos[i] = Util.rand(bound[0], bound[1]);
            pbest[i] = pos[i];                         //初始化粒子的个体最优
            v[i] = Util.rand(-0.6, 0.6);   //初始化粒子的速度
        }

        evaluate(fn);
        System.arraycopy(fitness, 0, pbest_fitness, 0, pbest_fitness.length);
        System.arraycopy(fitness, 0, gbest_fitness, 0, gbest_fitness.length);
//        pbest_fitness[0] = ReadFile.getFeatureNum(Process_NSPSOFS.name) + 1;
//        pbest_fitness[1] = 1;
//        gbest_fitness[0] = ReadFile.getFeatureNum(Process_NSPSOFS.name) + 1;
//        gbest_fitness[1] = 1;

        randnum1 = Util.rand(0, 1);
        randnum2 = Util.rand(0, 1);
    }

    /**
     * 评估函数值,同时记录历史最优位置
     *
     * @param fn 评估函数
     */
    public void evaluate(FN fn) {
        // 声明一个特征列表
        List<Object> features = fn.getFeatures(pos);

        double[] calculatedFitness;
        try {
            calculatedFitness = fn.doCalcFitness(features);
        } catch (IOException e) {
            log.error("evaluate occurred an error, e: ", e);
            return;
        }

        for (int i = 0; i < calculatedFitness.length; i++) {
            fitness[i] = calculatedFitness[i];
        }
//        fitness[1] = calculatedFitness[1];
//        if (features.size() == 0) {
//            fitness[1] = 1;
//        }
        //更新个体最优解
        if (beBetter()) {
            System.arraycopy(fitness, 0, pbest_fitness, 0, pbest_fitness.length);
            System.arraycopy(pos, 0, pbest, 0, pos.length);
        }
//        if (fitness[0] < pbest_fitness[0] && fitness[1] < pbest_fitness[1]) {
//            pbest_fitness[0] = fitness[0];
//            pbest_fitness[1] = fitness[1];
//            System.arraycopy(pos, 0, pbest, 0, pos.length);
//        } else if (fitness[0] == pbest_fitness[0] && fitness[1] < pbest_fitness[1]) {
//            pbest_fitness[0] = fitness[0];
//            pbest_fitness[1] = fitness[1];
//            System.arraycopy(pos, 0, pbest, 0, pos.length);
//        } else if (fitness[0] < pbest_fitness[0] && fitness[1] == pbest_fitness[1]) {
//            pbest_fitness[0] = fitness[0];
//            pbest_fitness[1] = fitness[1];
//            System.arraycopy(pos, 0, pbest, 0, pos.length);
//        }
    }

    private boolean beBetter() {
        boolean beBetter = true;
        for (int i = 0; i < fitness.length; i++) {
            if (fitness[i] < pbest_fitness[i]) {
                beBetter = false;
                break;
            }
        }
        return beBetter;
    }

    /**
     * 更新速度和位置
     */
    public void updateV() {
        for (int i = 0; i < pos.length; i++) {
            v[i] = w * v[i] + c1 * randnum1 * (pbest[i] - pos[i])
                    + c2 * randnum2 * (gbest[i] - pos[i]);
            if (v[i] > 0.6) {
                v[i] = Util.rand(-0.6, 0.6);
            }
            if (v[i] < -0.6) {
                v[i] = Util.rand(-0.6, 0.6);
            }
            pos[i] = (pos[i] + v[i]);
            if (pos[i] > 1) {
                pos[i] = Util.rand(0, 1);
            }
            if (pos[i] < 0) {
                pos[i] = Util.rand(0, 1);
            }
        }
    }

    public void mutate(int remainRunTimes, int totalRunTimes) {
        float fn = getFn(remainRunTimes, totalRunTimes);
        for (int i = 0; i < pos.length; i++) {
            pos[i] = pos[i] + fn * inverseCumulativeProbability(X_MIN, X_MAX);
            if (pos[i] < X_MIN) {
                pos[i] = X_MIN;
            } else if (pos[i] > X_MAX) {
                pos[i] = X_MAX;
            }
        }
    }

    private float getFn(int curRunTimes, int totalRunTimes) {
        float diff = ((float) totalRunTimes - curRunTimes) / totalRunTimes;
        return diff * diff;
    }

}
